import { Request, Response, NextFunction } from 'express';
import { getLogger } from '@/utils/logger';
import AsyncManager from '@/utils/async/AsyncManager';
import AsyncFactory from '@/utils/async/AsyncFactory';
import { BusinessStatus } from '@/decorators/Log';
import { getClientIp, setCurrentRequest } from '@/utils/ip/ip.util';
import { setCurrentRequest as setUserAgentRequest } from '@/utils/browser/user-agent.util';
import 'reflect-metadata';

// 操作日志记录器
const logger = getLogger('oper-log');

// 创建一个新的Map来存储请求处理的开始时间
const requestStartTimeMap = new Map<string, number>();

// 排除敏感属性字段
const EXCLUDE_PROPERTIES = ['password', 'oldPassword', 'newPassword', 'confirmPassword'];

// 定义操作日志接口
interface IOperLog {
  title: string;
  businessType: number;
  method: string;
  requestMethod: string;
  operatorType: number;
  operName: string;
  deptName: string;
  operUrl: string;
  operIp: string;
  operLocation: string;
  status: BusinessStatus;
  costTime: number;
  operTime: Date;
  operParam?: string;
  jsonResult?: string;
  errorMsg?: string;
}

/**
 * 操作日志中间件 - 处理请求前
 */
export function operLogBefore(req: Request, res: Response, next: NextFunction) {
  try {
    // 设置当前请求到IP工具和UserAgent工具
    setCurrentRequest(req);
    setUserAgentRequest(req);
    
    // 生成请求ID作为键
    const requestId = `${req.method}-${req.originalUrl}-${Date.now()}`;
    
    // 记录请求开始时间
    requestStartTimeMap.set(requestId, Date.now());
    
    // 将requestId存储在请求对象中，以便在响应中使用
    (req as any).operLogRequestId = requestId;
    
    next();
  } catch (error) {
    logger.error('操作日志前置处理失败', error);
    next();
  }
}

/**
 * 操作日志中间件 - 处理响应后
 */
export function operLogAfter(req: Request, res: Response, next: NextFunction) {
  // 保存原始的 end 方法
  const originalEnd = res.end;
  let responseBody: any;
  
  // 重写 end 方法以捕获响应体
  const newEnd = function(this: Response, chunk: any, encoding?: string | (() => void), cb?: () => void) {
    if (chunk && typeof chunk !== 'function') {
      responseBody = chunk.toString();
    }
    
    // 处理回调函数
    if (typeof encoding === 'function') {
      cb = encoding;
      encoding = undefined;
    }
    
    // 调用原始的 end 方法
    return originalEnd.call(this, chunk, encoding as BufferEncoding, cb);
  };
  
  res.end = newEnd;
  
  // 监听响应完成事件
  res.on('finish', () => {
    const requestId = (req as any).operLogRequestId;
    if (!requestId) {
      return;
    }
    
    // 获取请求开始时间
    const startTime = requestStartTimeMap.get(requestId);
    if (!startTime) {
      return;
    }
    
    // 删除requestId，防止内存泄漏
    requestStartTimeMap.delete(requestId);
    
    try {
      // 处理操作日志
      handleOperLog(req, res, responseBody, Date.now() - startTime);
    } catch (error) {
      logger.error('操作日志记录失败', error);
    }
  });
  
  next();
}

/**
 * 处理操作日志记录
 */
function handleOperLog(req: Request, res: Response, responseBody: any, costTime: number) {
  if (!(req as any).routeMeta) {
    return;
  }
  console.log("================== handleOperLog ===============================");
  console.log("routeMeta", (req as any).routeMeta);
  
  // 获取控制器方法和方法名
  const handler = (req as any).routeMeta?.controller;
  const methodName = (req as any).routeMeta?.methodName;
  
  // 如果找不到控制器方法或方法名，则不记录日志
  if (!handler || !methodName) {
    console.log("未找到控制器方法或方法名，不记录日志");
    return;
  }
  
  // 获取控制器类的构造函数
  const controllerClass = handler.classReference;
  if (!controllerClass) {
    console.log("未找到控制器类，不记录日志");
    return;
  }
  
  // 获取方法上的日志注解配置
  const logs: Record<string, any> = Reflect.getMetadata('operation:log', controllerClass) || {};
  const logConfig = logs[methodName];
  
  // 如果没有日志注解，则不记录日志
  if (!logConfig) {
    console.log("未找到日志注解配置，不记录日志");
    return;
  }
  
  // 获取用户信息（从JWT或会话中）
  const user = (req as any).user || {};
  
  // 构建操作日志对象
  const operLog: IOperLog = {
    title: logConfig.title,
    businessType: logConfig.businessType,
    method: `${controllerClass.name}.${methodName}()`,
    requestMethod: req.method,
    operatorType: logConfig.operatorType,
    operName: user.userName || '',
    deptName: user.deptName || '',
    operUrl: req.originalUrl,
    operIp: getClientIp(),
    operLocation: '', // 将通过异步任务获取地理位置
    status: res.statusCode >= 200 && res.statusCode < 300 ? BusinessStatus.SUCCESS : BusinessStatus.FAIL,
    costTime: costTime,
    operTime: new Date()
  };
  
  // 是否保存请求参数
  if (logConfig.isSaveRequestData) {
    // 获取请求参数并过滤敏感信息
    const requestParams = { ...req.query, ...req.body };
    
    // 过滤敏感字段
    for (const key of [...EXCLUDE_PROPERTIES, ...(logConfig.excludeParamNames || [])]) {
      if (requestParams[key]) {
        requestParams[key] = '******';
      }
    }
    
    operLog.operParam = JSON.stringify(requestParams).substring(0, 2000);
  }
  
  // 是否保存响应结果
  if (logConfig.isSaveResponseData && responseBody) {
    try {
      let jsonResult = typeof responseBody === 'string' ? responseBody : JSON.stringify(responseBody);
      operLog.jsonResult = jsonResult.substring(0, 2000);
    } catch (e) {
      operLog.jsonResult = '响应结果解析失败';
    }
  }
  
  // 如果请求失败，记录错误信息
  if (operLog.status === BusinessStatus.FAIL) {
    const errorMsg = (res as any).locals?.errorMessage || '请求处理失败';
    operLog.errorMsg = errorMsg.substring(0, 2000);
  }
  
  // 使用异步任务记录操作日志
  AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
}

export default {
  operLogBefore,
  operLogAfter
}; 